/*
 * init_lhdfs - Initial the lhdfs
 * @param uri      the palce where is the filesystem placed
 * @param fs_name  the name of the filesystem
 * @return Return a handle to the lhdfs
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <glib.h>
#include "hlfs.h"
#include "misc.h"
#include "comm_define.h"
#if 1
void * get_storage_access_handler(const char* uri,const char *fs_name){
     void * storage_handler = NULL;
     gchar **v  = g_strsplit(uri,"://",2);
     if (0==g_strcmp0(v[0],"local")){
         gchar * fs_path = g_build_filename(v[1],fs_name,NULL);
         g_message( "local [fs:%s] [path%s]\n",fs_name,fs_path);
         storage_handler = (void*)fs_path;
         if (!g_file_test (fs_path, G_FILE_TEST_IS_DIR)){
             g_strfreev(v);
             return NULL;
         }
     }else if(0 == g_strcmp0(v[0],"hdfs")){
         g_message( "hdfs storage not implement now \n");
     }else{
         g_message( "Error URI \n");
         g_strfreev(v);
         return NULL;
     }
     g_strfreev(v);
     return storage_handler;
}
#endif 

int read_fs_superblock(void * storage_handler,struct super_block *sb){
    int ret = 0;
    gchar * fs_path = (gchar*)storage_handler;
    gchar *sb_file_path = g_build_filename(fs_path,"superblock",NULL);
    GKeyFile * sb_keyfile = g_key_file_new();
    if (!g_file_test(sb_file_path, G_FILE_TEST_EXISTS) || !g_file_test(sb_file_path,G_FILE_TEST_IS_REGULAR)){
        g_message( "superblock file %s can not be accessable \n",sb_file_path);
        ret = -1;
        goto out;
    }  
    if(FALSE == g_key_file_load_from_file(sb_keyfile,sb_file_path,G_KEY_FILE_NONE,NULL)){
       g_message( "superblock file %s format is not key value pairs \n",sb_file_path);
       ret = -2;
       goto out;
    }
    gchar * fs_name =  g_key_file_get_string(sb_keyfile,"METADATA","fs_name",NULL);
    guint32 seg_size = g_key_file_get_integer(sb_keyfile,"METADATA","segment_size",NULL);
    guint32 block_size = g_key_file_get_integer(sb_keyfile,"METADATA","block_size",NULL);
   
    if(fs_name==NULL || seg_size == 0 || block_size ==0 ){
       g_message( "superblock parse error \n");
       g_free(fs_name);
       ret = -3;
       goto out;
    }
    sb->seg_size = seg_size;
    sb->block_size = block_size;
    g_strlcpy(sb->fsname,fs_name,MAX_FILE_NAME_LEN);
    g_message( "superblock : seg size :%u block size :%d fsname :%s\n",seg_size,block_size,fs_name);
out:
    g_free(sb_file_path);
    g_key_file_free(sb_keyfile);
    return ret;
}

static int get_latest_write_info(void* storage_handler,guint64* segno,guint64 *offset){
    int ret = 0;
    gchar * filename = NULL;
    gchar * file_path = NULL;
    struct stat buf;
    time_t latest_st_mtime = 0;
    gchar * latest_file = NULL;
    char * path = (char*)storage_handler;
    GDir * dir = g_dir_open(path,0,NULL);
    uint32_t max_seg = 0;
    while((filename = g_dir_read_name(dir)) != NULL) {
        file_path = g_build_filename(path,filename,NULL);
        g_message( "find filename:%s dir:%s filepath:%s\n",filename,path,file_path);
        if(g_str_has_suffix(file_path,"seg")){
#if 1       
            int res=lstat(file_path,&buf);
            if(res != 0)
            {
                g_message("errno %d \n",errno);
                ret = -1;
                goto out;
            }
            if( latest_st_mtime < buf.st_mtime){
                latest_st_mtime = buf.st_mtime;
                latest_file = filename;
                *offset = buf.st_size;
            }
#else
           gchar **v = g_strsplit(filename,".",2);
           //*segno = atol(v[1]);
           g_message("seg no %d\n",atoi(v[0]));
           if(max_seg =< atoi(v[0])){
              max_seg = atoi(v[0]);
           }
#endif
        }
    }
    if(latest_file!=NULL){
        g_message("latest file\n",latest_file);
        gchar *basename = g_basename(latest_file);
        gchar **v = g_strsplit(basename,".",2);
        *segno = atol(v[1]);
        g_strfreev(v);
    }else{
        *segno = 0;
    }
out:
    //g_free(latest_file);
    //g_free(filename);
    g_free(file_path);
    g_dir_close(dir);
    return ret;
}


static load_latest_inode_map_entry(struct hlfs_ctrl * ctrl){
   
   int ret = 0;
   char *segfile_name = build_segfile_name(ctrl->last_segno);
   char *segfile_path = g_build_filename((char*)ctrl->storage_handler,segfile_name,NULL);
   int fd = open(segfile_path,O_RDONLY);
   if (fd<0){
       g_message("can not open segment file %s\n",segfile_path);
       ret = -1;
       goto out2;
    }
   uint64_t inode_map_entry_pos = ctrl->last_offset - 
                        sizeof(struct inode_map_entry);
   g_message("inode map entry pos %llu\n",inode_map_entry_pos);
   if(sizeof(struct inode_map_entry) != 
            pread(fd,&ctrl->imap_entry,
                 sizeof(struct inode_map_entry),
                 inode_map_entry_pos)){
       g_message("can not read inode map entry \n");
       ret = -1;
       goto out1;
    }

out1:
    close(fd);
out2:
    g_free(segfile_name);
    g_free(segfile_path);
    return ret;
}


uint64_t  SEGMENT_SIZE_MASK = 0;
uint64_t  SEGMENT_SIZE_SHIFT = 0;

struct hlfs_ctrl *
init_hlfs(const char *uri, const char *fs_name)
{
    int ret;
    struct hlfs_ctrl *ctrl = (struct hlfs_ctrl*)g_malloc0(sizeof(struct hlfs_ctrl));
    if (!ctrl) {
	    g_message("%s -- allocate error!\n", __func__);
	    return NULL;
    }
    if(uri == NULL && fs_name == NULL){
        g_message( "input error \n");
        return NULL;
    }
    void * _storage_handler = get_storage_access_handler(uri,fs_name);
    if(_storage_handler==NULL){
        g_message( "[uri:%s] [fs:%s] can not accessable\n",uri,fs_name);
        g_free(ctrl);
        return NULL;
    }
    g_message( "storage_handler is %s\n",(char*)_storage_handler);
    ctrl->storage_handler = _storage_handler;
    gchar **v = g_strsplit(uri,"://",2);
    if (0==g_strcmp0(v[0],"local")){
        if(0!= read_fs_superblock(ctrl->storage_handler,&ctrl->sb)){
            g_message( "[uri:%s] [fs:%s] read superblock failed\n",uri,fs_name);
            g_free(ctrl);
            ctrl = NULL;
            goto out;
        }
    }else{
        g_message( " not implement now \n");
        g_free(ctrl);
        ctrl = NULL;
        goto out;
    }
    guint64  segno=0;
    guint64  offset = 0;
    if( 0!=get_latest_write_info(ctrl->storage_handler,&segno,&offset)){
        g_free(ctrl);
        ctrl = NULL;
        goto out;
    }
    g_message( "segno %llu,offset %llu\n",segno,offset);
    ctrl->last_segno = segno;
    ctrl->last_offset = offset;
    if(ctrl->last_segno != 0 || ctrl->last_offset != 0){
        if(0!=load_latest_inode_map_entry(ctrl)){
            g_message( "load inode map entry failed\n");
            ret = -1;
            goto out;
        }
    }
    uint64_t segment_size = ctrl->sb.seg_size;
    SEGMENT_SIZE_MASK  = segment_size - 1;
    SEGMENT_SIZE_SHIFT = 0;
    while (0 != (segment_size = (segment_size >> 1)))
    {                                                                                                         
        SEGMENT_SIZE_SHIFT++;
    }
out:
    g_strfreev(v);
    return ctrl;
}   

